#include <stdio.h>
#include <stdlib.h>
#include <jpeglib.h>

void create_thumbnail(const char *input_filename, const char *output_filename, int thumbnail_width, int thumbnail_height) {
    struct jpeg_decompress_struct cinfo;
    struct jpeg_error_mgr jerr;

    // Set up error handling
    cinfo.err = jpeg_std_error(&jerr);
    jpeg_create_decompress(&cinfo);

    // Open input JPEG file
    FILE *infile = fopen(input_filename, "rb");
    if (infile == NULL) {
        fprintf(stderr, "Cannot open %s\n", input_filename);
        return;
    }
    printf("Opened input file: %s\n", input_filename);

    // Read JPEG file header
    jpeg_stdio_src(&cinfo, infile);
    jpeg_read_header(&cinfo, TRUE);
    jpeg_start_decompress(&cinfo);

    // Get original image dimensions
    int original_width = cinfo.output_width;
    int original_height = cinfo.output_height;
    printf("Original dimensions: %dx%d\n", original_width, original_height);

    // Calculate scaling factors
    float width_ratio = (float)thumbnail_width / original_width;
    float height_ratio = (float)thumbnail_height / original_height;
    float ratio = (width_ratio < height_ratio) ? width_ratio : height_ratio;

    // Calculate new dimensions for the thumbnail
    int new_width = (int)(original_width * ratio);
    int new_height = (int)(original_height * ratio);
    printf("Thumbnail dimensions: %dx%d\n", new_width, new_height);

    // Allocate memory for the thumbnail
    unsigned char *thumbnail = (unsigned char *)malloc(new_width * new_height * cinfo.output_components);
    if (!thumbnail) {
        fprintf(stderr, "Memory allocation failed\n");
        jpeg_destroy_decompress(&cinfo);
        fclose(infile);
        return;
    }

    // Allocate memory for a buffer to hold one scanline of the original image
    unsigned char *buffer = (unsigned char *)malloc(original_width * cinfo.output_components);
    if (!buffer) {
        fprintf(stderr, "Memory allocation for buffer failed\n");
        free(thumbnail);
        jpeg_destroy_decompress(&cinfo);
        fclose(infile);
        return;
    }

    // First, read all original scanlines into memory
    unsigned char *original_image = (unsigned char *)malloc(original_width * original_height * cinfo.output_components);
    if (!original_image) {
        fprintf(stderr, "Memory allocation for original image failed\n");
        free(thumbnail);
        free(buffer);
        jpeg_destroy_decompress(&cinfo);
        fclose(infile);
        return;
    }

    // Read all scanlines from the original image
    int row_stride = original_width * cinfo.output_components;
    for (int y = 0; y < original_height; y++) {
        unsigned char *row_pointer[1];
        row_pointer[0] = &original_image[y * row_stride];
        (void)jpeg_read_scanlines(&cinfo, row_pointer, 1);
    }

    // Now resize the image using nearest neighbor interpolation
    for (int y = 0; y < new_height; y++) {
        int original_y = (int)(y / ratio);
        if (original_y >= original_height) original_y = original_height - 1;
        
        for (int x = 0; x < new_width; x++) {
            int original_x = (int)(x / ratio);
            if (original_x >= original_width) original_x = original_width - 1;
            
            for (int c = 0; c < cinfo.output_components; c++) {
                thumbnail[(y * new_width + x) * cinfo.output_components + c] = 
                    original_image[(original_y * original_width + original_x) * cinfo.output_components + c];
            }
        }
    }

    // Clean up after decompression
    jpeg_finish_decompress(&cinfo);
    fclose(infile);
    free(buffer);
    free(original_image);

    // Save the thumbnail
    FILE *outfile = fopen(output_filename, "wb");
    if (outfile == NULL) {
        fprintf(stderr, "Cannot open %s\n", output_filename);
        free(thumbnail);
        return;
    }
    printf("Saving thumbnail to: %s\n", output_filename);

    // Set up JPEG compression parameters
    struct jpeg_compress_struct cinfo_out;
    cinfo_out.err = jpeg_std_error(&jerr);
    jpeg_create_compress(&cinfo_out);
    jpeg_stdio_dest(&cinfo_out, outfile);

    cinfo_out.image_width = new_width;
    cinfo_out.image_height = new_height;
    cinfo_out.input_components = cinfo.output_components;
    cinfo_out.in_color_space = cinfo.out_color_space;

    jpeg_set_defaults(&cinfo_out);
    jpeg_set_quality(&cinfo_out, 90, TRUE); // Set quality

    // Start compression
    jpeg_start_compress(&cinfo_out, TRUE);

    // Write the thumbnail data
    for (int i = 0; i < new_height; i++) {
        unsigned char *row_pointer[1];
        row_pointer[0] = &thumbnail[i * new_width * cinfo_out.input_components];
        int written = jpeg_write_scanlines(&cinfo_out, row_pointer, 1);
        if (written < 1) {
            fprintf(stderr, "Error writing scanline %d\n", i);
            break;
        }
    }

    // Finish compression
    jpeg_finish_compress(&cinfo_out);
    fclose(outfile);
    free(thumbnail);
    jpeg_destroy_compress(&cinfo_out);
    printf("Thumbnail created successfully!\n");
}

int main(int argc, char *argv[]) {
    if (argc < 5) {
        fprintf(stderr, "Usage: %s <input.jpg> <output.jpg> <width> <height>\n", argv[0]);
        return 1;
    }

    const char *input_filename = argv[1];
    const char *output_filename = argv[2];
    int thumbnail_width = atoi(argv[3]);
    int thumbnail_height = atoi(argv[4]);

    create_thumbnail(input_filename, output_filename, thumbnail_width, thumbnail_height);
    return 0;
} 


/*
还不能正常运行
gcc create_thumbnail.c -o create_thumbnail -ljpeg
./create_thumbnail input.jpg output.jpg 100 100
*/